using System;
using System.Collections.Generic;
using System.Text;

namespace WOWEquipOptimizer.Shaman
{
    class Enhancement : MeleeDD
    {
        public Enhancement()
            : base()
        {
            AllowTwoHand = false;
            AllowDualWield = true;
            m_Base = new List<Attribute>();
            m_Base.Add(new Attribute(AttributeName.Health, 3000));
            m_Base.Add(new Attribute(AttributeName.Stamina, 116));
            m_Base.Add(new Attribute(AttributeName.Strength, 107));
            m_Base.Add(new Attribute(AttributeName.AttackPower, 120));
            m_Base.Add(new Attribute(AttributeName.CritRating, 1.67f * CritRating));
            m_Base.Add(new Attribute(AttributeName.Agility, 59));
            // talents now
            m_Base.Add(new Attribute(AttributeName.CritRating, 5 * CritRating));
            m_Base.Add(new Attribute(AttributeName.HitRating, 9 * HitRating));
            foreach (Attribute attr in m_Base)
            {
                handleAttribute(attr);
            }
            Buff goa = new Buff("Grace of Air", new List<Attribute>());
            goa.Attributes.Add(new Attribute(AttributeName.Agility, 77 * 1.15f));
            Buffs.Add(goa);
            Buff soe = new Buff("Strength of Earth",new List<Attribute>());
            soe.Attributes.Add(new Attribute(AttributeName.Strength, 88 * 1.15f));
            Buffs.Add(soe);
        }
        // WDPS1, WDPS2, WS1, WS2, Crit,Hit, AP, Haste,health,
        public override void handleAttribute(Attribute attr)
        {
            switch (attr.Type)
            {
                case AttributeName.Agility:
                    m_Equip[AttributeName.CritRating] += (attr.Value * (HasBlessingOfKings ? 1.1f : 1.0f) / 25) * CritRating;
                    break;
                case AttributeName.CritMeleeRating:
                case AttributeName.CritRating:
                    m_Equip[AttributeName.CritRating] += attr.Value;                 
                    break;
                case AttributeName.HitMeleeRating:
                case AttributeName.HitRating:
                    m_Equip[AttributeName.HitRating] += attr.Value;
                    break;
                case AttributeName.Strength:
                    m_Equip[AttributeName.AttackPower] += attr.Value * (HasBlessingOfKings ? 1.1f : 1.0f) * 2;
                    break;
                case AttributeName.HasteDirect:
                    m_Equip[attr.Type] = (((1 + (m_Equip[attr.Type] / HasteRating) / 100f) * (1 + attr.Value / 100)) - 1) * HasteRating * 100;
                    break;
                default:
                    base.handleAttribute(attr);
                    break;
            }
        }
        public override float evaluateTF()
        {
            float wdps1 = (float)m_Equip[AttributeName.MHWDPS];
            float wdps2 = (float)m_Equip[AttributeName.OHWDPS];
            float ws1 = (float)m_Equip[AttributeName.MHWS];
            float ws2 = (float)m_Equip[AttributeName.OHWS];
            float crit = (float)m_Equip[AttributeName.CritRating] / CritRating;
            float hit = (float)m_Equip[AttributeName.HitRating] / HitRating;
            float baseap = (float)m_Equip[AttributeName.AttackPower];
            float haste = (float)m_Equip[AttributeName.HasteRating] / HasteRating;
            /*
            float wdps1 = (395 + 196) / 5.6f;
            float wdps2 = (395 + 196) / 5.6f;
            float ws1 = 2.8f;
            float ws2 = 2.8f;
            float crit = 35f;
            float hit = 19f;
            float baseap = 2500;
            float haste = 11;
            */
            if (wdps1 > 0 && ws1 > 0 && wdps2 > 0 && ws2 > 0)
            {
                // calculation below, all values in percent unless noted otherwise
                float glancingreduction = 25; // XX% reduced damage
                float glancingchance = 25; // chance to happen == XX%
                float glancing = glancingreduction * glancingchance / 100; // this value is used in the closed-form of the attacktable below
                float dodge = 5.6f; // 5.6% dodge chance of the boss
                float hitwhite = (float)Math.Min(100 - 5 - 19 - 3.0f - 0.6f + hit, 100); // 100% - 5% base - 19% dw - 3% bossmalus- 0.6% wskilldifference + hit, upto array maximum of 100
                float hityellow = (float)Math.Min(100 - 5 - 3.0f - 0.6f + hit, 100);// 100% - 5% base - 3% bossmalus - 0.6% wskilldifference + hit, upto array maximum of 100
                float avghit = (hityellow + hitwhite) / 200; // average hit value, already normalized to 0<=x<=1
                float noncrit = (1 - crit / 100 ); // the chance not to crit
                // weapon speed incl.flurry
                // 30% * flurry uptime + possible haste effects, calculating the effective speed
                //float flurryprob = Math.Min((1 - (float)Math.Pow(noncrit, 3)) + crit / 100 * (2 * 0.18f + 1 / 20f * ws1 / 1.1f + 1 / 20f * ws2 / 1.1f), 1.0f);
                float minwftime = 3.0f;
                float flurryvalue = 0.30f;
                float wfprocprob = 0.36f;
                if (this.SpecialValues.Contains("4947067"))
                {
                    flurryvalue = 0.35f;
                }
                float attackspeed = (ws1 + ws2) / (4 * (1 + flurryvalue) * (1 + haste / 100)); // attackspeed while flurried
                float ssinthree = 3 * attackspeed / 5;
                // still approximated, probability to not crit in 3+(1-notwfinthree)+ssinthree)
                //float wftime = (attackspeed / (wfprocprob / 2f)) / ((attackspeed / (wfprocprob / 2f)) + 3);
                float wftime = 1 / (1 + 1.5f * (wfprocprob / attackspeed));
                float flurryprob = (float)Math.Min(1 - Math.Pow(noncrit, 3f + 1 - Math.Pow(1 - wfprocprob, wftime * 3) + ssinthree), 1.0);
                float wsmh = (ws1 / (1 + haste / 100)) * (1 - flurryprob) + (ws1 / ((1 + haste / 100) * (1 + flurryvalue))) * flurryprob;
                float wsoh = (ws2 / (1 + haste / 100)) * (1 - flurryprob) + (ws2 / ((1 + haste / 100) * (1 + flurryvalue))) * flurryprob;
                float avgspeed = (wsmh + wsoh) / 2; // average again for some stuff
                // windfury, already normalized to 0<=x<=1
                // we assume an wf proc every 
                // float wfnerf = 0.1f + ((0.2f - 0.1f) / (2.8f - 1.0f)) * (Math.Min(avgspeed / avghit, 2.8f) - 1.0f);
                // expected wf time is avgspeed/0.36f, minimum is 3.1s
                float exwftime = Math.Max(avgspeed / wfprocprob, minwftime);
                float wfproc = avgspeed / exwftime; // proc rate
                float wfprocmh = ws2 / (ws1 + ws2) * wfproc; // corrected 
                float wfprocoh = ws1 / (ws1 + ws2) * wfproc;
                // stormstrike, stormstrike is basically a weaponspeed/10 increase of dps
                float ssmh = 1 + (hityellow / (hitwhite - glancing)) * (wsmh / 10);
                float ssoh = 1 + (hityellow / (hitwhite - glancing)) * (wsoh / 10);
                // haste is array factor just as stormstrike
                float hastemh = ws1 / wsmh;
                float hasteoh = ws2 / wsoh;
                // tsunami talisman
                if (this.SpecialValues.Contains("22071316"))
                {
                    baseap += Math.Min(10.0f / avgspeed * (crit / 100f) * 0.1f * 340f, 340f);
                }
                // attack power including unleashed rage:
                // hits in 10 basically calculates the amount of hits done in 10s 
                float hitsin10 = 1 / 100f * ((hitwhite - glancingchance - dodge) * (10 / avgspeed) + (hitwhite - dodge) * (hityellow - dodge) * 1 / 100f * 0.36f * 2f + (hityellow - dodge) * 2f);
                float ap = baseap * (1 + 0.1f * (1 - (float)Math.Pow(noncrit, (hitsin10))));
                float wfap = (475 + ap);
                if (this.SpecialValues.Contains("18047328"))
                {
                    wfap += 80;
                }
                // closed form of an attack table, parenthesis are used to calculate the actual dpsfactor
                float mhwhitedps = 1 / 100f * (hitwhite + crit - glancing - dodge) * (wdps1 + ap / 14);
                float mhyellowdps = 1 / 100f * 1 / 100f * (hitwhite - dodge) * (hityellow + crit - dodge) * 2f * wfprocmh * 1.4f * (wdps1 + wfap / 14);
                float mhdps = 1.1f * ssmh * hastemh * (mhwhitedps + mhyellowdps);
                float ohwhitedps = 0.5f * 1 / 100f * (hitwhite + crit - glancing - dodge) * (wdps2 + ap / 14);
                float ohyellowdps = 0.5f * 1 / 100f * 1 / 100f * (hitwhite - dodge) * (hityellow + crit - dodge) * 2f * wfprocoh * 1.4f * (wdps1 + (2 * wfap) / 14); // apparently OH WF AP bonus is not reduced, so we add it again
                float ohdps = 1.1f * ssoh * hasteoh * (ohwhitedps + ohyellowdps);
                float finaldps = Character.Armor * (mhdps + ohdps);
                return CondFactor * finaldps;
            }
            else return -1;
        }
         public float evaluateTF2()
        {
            float wdps1 = (float)m_Equip[AttributeName.MHWDPS];
            float wdps2 = (float)m_Equip[AttributeName.OHWDPS];
            float ws1 = (float)m_Equip[AttributeName.MHWS];
            float ws2 = (float)m_Equip[AttributeName.OHWS];
            float crit = (float)m_Equip[AttributeName.CritRating] / CritRating;
            float hit = (float)m_Equip[AttributeName.HitRating] / HitRating;
            float baseap = (float)m_Equip[AttributeName.AttackPower];
            float haste = (float)m_Equip[AttributeName.HasteRating] / HasteRating;
            /*
            float wdps1 = (312 + 167) / 5.2f;
            float wdps2 = (276 + 184) / 5.2f;
            float ws1 = 2.6f;
            float ws2 = 2.6f;
            float crit = 25f;
            float hit = 22f;
            float baseap = 2200;
            float haste = 0;
            */
            if (wdps1 > 0 && ws1 > 0 && wdps2 > 0 && ws2 > 0)
            {
                // calculation below, all values in percent unless noted otherwise
                float glancingreduction = 25; // XX% reduced damage
                float glancingchance = 25; // chance to happen == XX%
                float glancing = glancingreduction * glancingchance / 100; // this value is used in the closed-form of the attacktable below
                float dodge = 5.6f; // 5.6% dodge chance of the boss
                float hitwhite = (float)Math.Min(100 - 5 - 19 - 3.0f - 0.6f + hit, 100); // 100% - 5% base - 19% dw - 3% bossmalus- 0.6% wskilldifference + hit, upto array maximum of 100
                float hityellow = (float)Math.Min(100 - 5 - 3.0f - 0.6f + hit, 100);// 100% - 5% base - 3% bossmalus - 0.6% wskilldifference + hit, upto array maximum of 100
                float avghit = (hityellow + hitwhite) / 200; // average hit value, already normalized to 0<=x<=1
                float noncrit = (1 - crit / 100 ); // the chance not to crit
                // weapon speed incl.flurry
                // 30% * flurry uptime + possible haste effects, calculating the effective speed
                //float flurryprob = Math.Min((1 - (float)Math.Pow(noncrit, 3)) + crit / 100 * (2 * 0.18f + 1 / 20f * ws1 / 1.1f + 1 / 20f * ws2 / 1.1f), 1.0f);
                float minwftime = 3.0f;
                float flurryvalue = 0.30f;
                float wfprocprob = 0.2f;
                if (this.SpecialValues.Contains("4947067"))
                {
                    flurryvalue = 0.35f;
                }
                float attackspeed = (ws1 + ws2) / (4 * (1 + flurryvalue) * (1 + haste / 100)); // attackspeed while flurried
                float ssinthree = 3 * attackspeed / 10;
                // still approximated, probability to not crit in 3+(1-notwfinthree)+ssinthree)
                //float wftime = (attackspeed / (wfprocprob / 2f)) / ((attackspeed / (wfprocprob / 2f)) + 3);
                float wftime = 1 / (1 + 1.5f * (wfprocprob / attackspeed));
                float flurryprob = (float)Math.Min(1 - Math.Pow(noncrit, 3f + 1 - Math.Pow(1 - wfprocprob, wftime * 3) + ssinthree), 1.0);
                float wsmh = (ws1 / (1 + haste / 100)) * (1 - flurryprob) + (ws1 / ((1 + haste / 100) * (1 + flurryvalue))) * flurryprob;
                float wsoh = (ws2 / (1 + haste / 100)) * (1 - flurryprob) + (ws2 / ((1 + haste / 100) * (1 + flurryvalue))) * flurryprob;
                float avgspeed = (wsmh + wsoh) / 2; // average again for some stuff
                // windfury, already normalized to 0<=x<=1
                // we assume an wf proc every 
                // float wfnerf = 0.1f + ((0.2f - 0.1f) / (2.8f - 1.0f)) * (Math.Min(avgspeed / avghit, 2.8f) - 1.0f);
                // expected wf time is avgspeed/0.36f, minimum is 3.1s
                float exwftime = Math.Max(avgspeed / wfprocprob, minwftime);
                float wfproc = avgspeed / exwftime; // proc rate
                float wfprocmh = wfproc; // corrected 
                // stormstrike, stormstrike is basically a weaponspeed/10 increase of dps
                float ssmh = 1 + (hityellow / (hitwhite - glancing)) * (wsmh / 10);
                float ssoh = 1 + (hityellow / (hitwhite - glancing)) * (wsoh / 10);
                // haste is array factor just as stormstrike
                float hastemh = ws1 / wsmh;
                float hasteoh = ws2 / wsoh;
                // tsunami talisman
                if (this.SpecialValues.Contains("22071316"))
                {
                    baseap += Math.Min(10.0f / avgspeed * (crit / 100f) * 0.1f * 340f, 340f);
                }
                // attack power including unleashed rage:
                // hits in 10 basically calculates the amount of hits done in 10s 
                float hitsin10 = 1 / 100f * ((hitwhite - glancingchance - dodge) * (10 / avgspeed) + (hitwhite - dodge) * (hityellow - dodge) * 1 / 100f * 0.36f * 2f + (hityellow - dodge) * 2f);
                float ap = baseap * (1 + 0.1f * (1 - (float)Math.Pow(noncrit, (hitsin10))));
                float wfap = (475 + ap);
                if (this.SpecialValues.Contains("18047328"))
                {
                    wfap += 80;
                }
                // closed form of an attack table, parenthesis are used to calculate the actual dpsfactor
                float mhwhitedps = 1 / 100f * (hitwhite + crit - glancing - dodge) * (wdps1 + ap / 14);
                float mhyellowdps = 1 / 100f * 1 / 100f * (hitwhite - dodge) * (hityellow + crit - dodge) * 2f * wfprocmh * 1.4f * (wdps1 + wfap / 14);
                float mhdps = 1.1f * ssmh * hastemh * Character.Armor * (mhwhitedps + mhyellowdps);
                float ohwhitedps = 0.5f * 1 / 100f * (hitwhite + crit - glancing - dodge) * (wdps2 + ap / 14);
                float spellhit = 0.83f + 0.03f;
                float spellcrit = 1.05f;
                float ohyellowdps = 1 / 100f * (hitwhite - dodge) * spellhit * spellcrit * 1.15f * (35 * ws2 + ap * 0.03f)/wsoh; // apparently OH WF AP bonus is not reduced, so we add it again
                float ohdps = 1.1f * ssoh * hasteoh * (Character.Armor*ohwhitedps + ohyellowdps);
                float finaldps = mhdps + ohdps;
                return CondFactor * finaldps;
            }
            else return -1;
        }
        // 
        public override List<Item> filterEquip(List<Item> input)
        {
            input = base.filterEquip(input);
            List<Item> retval = new List<Item>();
            SetBlacklist.Add("Cryptstalker Armor");
            SetBlacklist.Add("Deathdealer's Embrace");
            SetBlacklist.Add("Bonescythe Armor");
            foreach (Item item in input)
            {
                if ((item.AllowableClasses & CharacterClass.Shaman) == 0) continue;
                if(item.GetType() == typeof(Weapon))
                {
                    Weapon weapon = (Weapon)item;
                    if (weapon.WeaponSlot == WeaponSlot.Ranged && weapon.Type != WeaponType.Totem) continue;
                    if (weapon.Type == WeaponType.Sword) continue;
                    if (weapon.TwoHand) continue;
                }
                else if(item.GetType() == typeof(Armor))
                {
                    Armor armor = (Armor)item;
                    if ((armor.Type & (ArmorProficiency.Leather | ArmorProficiency.Mail)) == 0) continue;                 
                    if (armor.Slot == ArmorSlot.OffHand) continue;
                }
                if (SetBlacklist.Contains(item.Set.Name)) continue;
                if (ItemBlacklist.Contains(item.Name)) continue;
                retval.Add(item);
            }
            return handleWhiteList(retval);
        }
        public override bool evaluateBuild(int[] build)
        {
            if (build[1] >= 41 && build[2] >= 13)
            {
                return true;
            }
            else return false;
        }
    }
}
 